home *** CD-ROM | disk | FTP | other *** search
- /*
- * ttyDriver.c --
- *
- * The routines in this module implement an emulator for the
- * UNIX 4.2 BSD tty driver. The emulation is done in a way
- * that is independent of the specific environme (kernel, user,
- * etc.) by using a set of callback procedures to interface to
- * a raw device on one side and a client on the "cooked" side..
- *
- * Copyright 1987, 1989 Regents of the University of California
- * Permission to use, copy, modify, and distribute this
- * software and its documentation for any purpose and without
- * fee is hereby granted, provided that the above copyright
- * notice appear in all copies. The University of California
- * makes no representations about the suitability of this
- * software for any purpose. It is provided "as is" without
- * express or implied warranty.
- */
-
- #ifndef lint
- static char rcsid[] = "$Header: /sprite/src/lib/c/etc/RCS/ttyDriver.c,v 1.19 90/09/11 14:17:06 kupfer Exp Locker: shirriff $ SPRITE (Berkeley)";
- #endif not lint
-
- #include <sprite.h>
- #include <dev/tty.h>
- #include <fs.h>
- #include <stdio.h>
- #include <ctype.h>
- #include <errno.h>
- #include <signal.h>
- #include <stdlib.h>
- #include <sys/ioctl.h>
- #include <fmt.h>
- #include "td.h"
-
- /*
- * The structure below corresponds to one terminal (one call to Td_Create).
- */
-
- typedef struct Terminal {
- /*
- * Controlling parameters for terminal. These all have exactly the
- * same meanings as in 4.3 BSD; see the 4.3 BSD terminal driver
- * documentation for details.
- */
-
- struct sgttyb sgttyb;
- struct tchars tchars;
- struct ltchars ltchars;
- int localMode;
- struct winsize winsize;
-
- /*
- * State of the terminal.
- */
-
- int column; /* Column where the next character will be
- * echoed (i.e., cursor position). Needed
- * in order to handle tabs correctly. */
- int owner; /* Identifier of controlling process or process
- * group (signals are sent here). */
- int numOpens; /* Number of opens that have been completed
- * successfully but not yet closed. */
- int flags; /* See below for definitions. */
-
- /*
- * Input buffer, indexed circularly. The buffer is reallocated
- * with a larger size if it ever fills up.
- */
-
- char *inputBuffer; /* Input buffer area (malloc'ed). */
- int inBufSize; /* Number of bytes in inputBuffer. */
- int lastAddedIn; /* Index of last character added to buffer. */
- int lastRemovedIn; /* Index of last place from which character
- * was removed from buffer. If lastAddedIn =
- * lastRemovedIn, the buffer is empty. */
- int lastBreak; /* Index of last break character (newline,
- * etc.) or lastRemovedIn if none in buffer. */
- int lastHidden; /* Value of lastAddedIn at the time the last
- * output to the terminal was done. Characters
- * before this can't be smoothly backspaced
- * over, because there's output in front of
- * them on the screen. -1 means there are no
- * editable characters in the buffer that are
- * hidden. */
-
- /*
- * In order to backspace correctly over weird things like tabs, the
- * following two variables keep track of an index position in the input
- * buffer, and the column just to the right of where that character was
- * echoed on the display. KeyIndex is either the same as lastBreak or
- * lastHidden or lastRemovedIn, and has two properties: a) it will
- * never be backspaced over in CRT mode; and b) no characters beyond
- * this one will be returned to the application until keyIndex is first
- * advanced.
- */
-
- int keyIndex;
- int keyColumn; /* Column just after keyIndex character. */
-
- /*
- * Output buffer, indexed circularly. This buffer also grows
- * dynamically if necessary (this is necessary so that new room can
- * be made for characters being echoed, even if the buffer was
- * previously "full"). However, the cooked side of the terminal
- * is marked "not ready for output" whenever there are more than
- * cookedOutputLimit characters in the buffer (but any call to
- * Td_PutCooked will always complete; it is up to the cooked-side
- * callbacks to stop calling Td_PutCooked). The goal here is to
- * keep the application from getting too many characters ahead of
- * the actual device.
- */
-
- char *outputBuffer; /* Output buffer area (malloc'ed). */
- int outBufSize; /* Number of bytes in outputBuffer. */
- int lastAddedOut; /* Index of last character added to
- * outputBuffer. */
- int lastRemovedOut; /* Index of last character removed from
- * outputBufer. */
- int outCharsBuffered; /* Number of characters bufferred in
- * outputBuffer. */
- int cookedOutputLimit; /* Mark cooked side not ready for output
- * whenever outCharsBuffered is greater
- * than this. */
-
- /*
- * Callback procedures and data provided by the client:
- */
-
- int (*cookedProc)_ARGS_((ClientData, int operation, int inBufSize,
- char *inBuffer, int outBufSize,
- char *outBuffer));
- /* Procedure to call to register change
- * in state of cooked-side interface. */
- ClientData cookedData; /* Value to pass to cookedProc. */
- int (*rawProc)_ARGS_((ClientData, int operation, int inBufSize,
- char *inBuffer, int outBufSize,
- char *outBuffer));
- /* Procedure to call to register change
- * in state of raw-side interface. */
- ClientData rawData; /* Value to pass to rawProc. */
- } Terminal;
-
- /* Flag values:
- *
- * EXCLUSIVE: No more opens should be allowed until terminal
- * has been completely closed.
- * BS_IN_PROGRESS: A printing backspace sequence (delimited by
- * "\" and "/") is in progress, and will eventually
- * need the closing "/".
- * LITERAL_NEXT: The next character should be taken literally,
- * and should be put into the input buffer with
- * no special interpretation.
- * OWNER_FAMILY: 1 means the owner is a process family. 0 means
- * it's a single process.
- * OUTPUT_OFF: 1 means output to the raw device has been stopped,
- * for example because ^S was typed.
- */
-
- #define EXCLUSIVE 0x1
- #define BS_IN_PROGRESS 0x2
- #define LITERAL_NEXT 0x4
- #define OWNER_FAMILY 0x8
- #define OUTPUT_OFF 0x10
-
- /*
- * Default values for tty parameters:
- */
-
- struct sgttyb sgttybDefault = {
- B9600, B9600, 010, 025, EVENP|ODDP|CRMOD|ECHO
- };
- struct tchars tcharsDefault = {
- 03, 034, 021, 023, 04, -1
- };
- struct ltchars ltcharsDefault = {
- 032, 031, 022, 017, 027, 026
- };
- int localModeDefault = LCRTBS|LCRTERA|LCRTKIL|LCTLECH;
- struct winsize winsizeDefault = {
- 0, 0, 0, 0
- };
-
- /*
- * Macros for moving buffer pointers forward and backward circularly.
- */
-
- #define NEXT(src, size, dst) \
- (dst) = (src)+1; \
- if ((dst) >= (size)) { \
- (dst) = 0; \
- }
-
- #define PREV(src, size, dst) \
- (dst) = (src)-1; \
- if ((dst) < 0) { \
- (dst) = (size)-1; \
- }
-
- int td_Debug = 0;
-
- /*
- * Forward declarations for procedures defined later in this file:
- */
-
- static void TdBackspace _ARGS_((Terminal *tPtr));
- static void TdEcho _ARGS_((Terminal *tPtr, int c));
- static void TdFlushInput _ARGS_((Terminal *tPtr));
- static void TdFlushOutput _ARGS_((Terminal *tPtr));
- static void TdPutChar _ARGS_((Terminal *tPtr, int c));
- static void TdRetypeInput _ARGS_((Terminal *tPtr, int start));
- static int FormatInput _ARGS_((int command, Fmt_Format format,
- int inputSize, Address input,
- int *newInputSizePtr, Address newInput));
- static int FormatOutput _ARGS_((int command, Fmt_Format format,
- int outputSize, Address output,
- int *newOutputSizePtr,
- Address newOutput));
-
-
- /*
- *----------------------------------------------------------------------
- *
- * Td_Create --
- *
- * This procedure creates and initializes a new terminal
- * driver.
- *
- * Results:
- * The return value is a handle that is used to refer to the
- * driver when calling other Td_ procedures, such as Td_Delete.
- *
- * Side effects:
- * The procedures cookedProc and rawProc may be invoked by
- * other procedures in this module at later times. See the
- * man page for details.
- *
- *----------------------------------------------------------------------
- */
-
- Td_Terminal
- Td_Create(bufferSize, cookedProc, cookedData, rawProc, rawData)
- int bufferSize; /* How much buffer space to allow on
- * output. */
- int (*cookedProc)_ARGS_((ClientData, int operation, int inBufSize,
- char *inBuffer, int outBufSize,
- char *outBuffer));
- /* Procedure to call for control operations
- * on cooked side of driver. */
- ClientData cookedData; /* Arbitrary value, provided by caller,
- * which will be passed to cookedProc
- * whenever it is invoked. */
- int (*rawProc)_ARGS_((ClientData, int operation, int inBufSize,
- char *inBuffer, int outBufSize,
- char *outBuffer));
- /* Procedure to call for control operations
- * on raw side of driver. */
- ClientData rawData; /* Arbitrary value, provided by caller,
- * which will be passed to rawProc whenever
- * it is invoked. */
- {
- register Terminal *tPtr;
- Td_BaudRate baud;
-
- tPtr = (Terminal *) malloc(sizeof(Terminal));
- tPtr->sgttyb = sgttybDefault;
- tPtr->tchars = tcharsDefault;
- tPtr->ltchars = ltcharsDefault;
- tPtr->localMode = localModeDefault;
- tPtr->winsize = winsizeDefault;
- tPtr->column = 0;
- tPtr->owner = -1;
- tPtr->flags = 0;
- tPtr->inputBuffer = (char *) malloc(100);
- tPtr->inBufSize = 100;
- tPtr->lastAddedIn = 0;
- tPtr->lastRemovedIn = 0;
- tPtr->lastBreak = 0;
- tPtr->lastHidden = -1;
- tPtr->keyIndex = 0;
- tPtr->keyColumn = 0;
- tPtr->outputBuffer = (char *) malloc(1000);
- tPtr->outBufSize = 1000;
- tPtr->lastAddedOut = 0;
- tPtr->lastRemovedOut = 0;
- tPtr->outCharsBuffered = 0;
- tPtr->cookedOutputLimit = bufferSize;
- tPtr->cookedProc = cookedProc;
- tPtr->cookedData = cookedData;
- tPtr->rawProc = rawProc;
- tPtr->rawData = rawData;
-
- /*
- * Fetch the actual baud rate from the raw device manager.
- */
-
- if ((*rawProc)(rawData, TD_RAW_GET_BAUD_RATE, 0, (char *) NULL,
- sizeof(baud), (char *) &baud) == sizeof(baud)) {
- tPtr->sgttyb.sg_ispeed = baud.ispeed;
- tPtr->sgttyb.sg_ospeed = baud.ospeed;
- }
-
- return (Td_Terminal) tPtr;
- }
-
- /*
- *----------------------------------------------------------------------
- *
- * Td_Delete --
- *
- * Close down a terminal driver, destroying all of the state
- * associated with it.
- *
- * Results:
- * None.
- *
- * Side effects:
- * A hangup is simulated on the cooked side of the driver, and
- * memory is released. The caller should never again use
- * terminal.
- *
- *----------------------------------------------------------------------
- */
-
- void
- Td_Delete(terminal)
- Td_Terminal terminal; /* Token identifying terminal (returned
- * by previous call to Td_Create). */
- {
- register Terminal *tPtr = (Terminal *) terminal;
-
- (*tPtr->rawProc)(tPtr->rawData, TD_RAW_SHUTDOWN, 0, (char *) NULL,
- 0, (char *) NULL);
- free((char *) tPtr->inputBuffer);
- free((char *) tPtr->outputBuffer);
- free((char *) tPtr);
- }
-
- /*
- *----------------------------------------------------------------------
- *
- * Td_Open --
- *
- * This procedure should be called before accepting a new
- * "open" for a terminal. It indicates whether new opens
- * are permitted.
- *
- * Results:
- * The return value is zero if the open is to be permitted,
- * and a non-zero errno value if it is to be denied. If the
- * open is successful, *selectBitsPtr is filled in with the
- * initial select state for the terminal.
- *
- * Side effects:
- * Information counting open streams on the terminal gets
- * updated.
- *
- *----------------------------------------------------------------------
- */
-
- int
- Td_Open(terminal, selectBitsPtr)
- Td_Terminal terminal; /* Token for the terminal to be
- * checked. */
- int *selectBitsPtr; /* Put initial select state here. */
- {
- register Terminal *tPtr = (Terminal *) terminal;
-
- if (tPtr->flags & EXCLUSIVE) {
- return EBUSY;
- }
- tPtr->numOpens++;
- *selectBitsPtr = 0;
- if ((tPtr->lastBreak != tPtr->lastRemovedIn)
- || (tPtr->localMode & LPENDIN)) {
- *selectBitsPtr |= FS_READABLE;
- }
- if (tPtr->outCharsBuffered < tPtr->cookedOutputLimit) {
- *selectBitsPtr |= FS_WRITABLE;
- }
- return 0;
- }
-
- /*
- *----------------------------------------------------------------------
- *
- * Td_Close --
- *
- * This procedure should be called whenever all of the I/O streams
- * associated with a single open on a terminal have been closed.
- *
- * Results:
- * none.
- *
- * Side effects:
- * State in the terminal is updated to reflect the close.
- *
- *----------------------------------------------------------------------
- */
-
- void
- Td_Close(terminal)
- Td_Terminal terminal; /* Token for the terminal to be
- * checked. */
- {
- register Terminal *tPtr = (Terminal *) terminal;
-
- tPtr->numOpens--;
- if (tPtr->numOpens == 0) {
- tPtr->flags &= ~EXCLUSIVE;
- }
- }
-
- /*
- *----------------------------------------------------------------------
- *
- * Td_GetCooked --
- *
- * Retrieve characters that are ready to be read from the cooked
- * side of a terminal driver.
- *
- * Results:
- * The return value is normally 0. If non-zero, it indicates
- * that an error occurred and holds a UNIX errno. Most often,
- * this is EWOULDBLOCK, meaning that no characters were
- * available. The value at numCharsPtr is overwritten with the
- * actual number of characters returned at buffer, and will be
- * 0 if an error or end-of-file occurred. *SigNumPtr is overwritten
- * with a signal to send to the invoking process, or 0. *SelectBitsPtr
- * is updated to reflect the readability of the terminal.
- *
- * Side effects:
- * Characters are removed from the terminal's input buffer.
- *
- *----------------------------------------------------------------------
- */
-
- int
- Td_GetCooked(terminal, pID, familyID, numCharsPtr, buffer,
- sigNumPtr, selectBitsPtr)
- Td_Terminal terminal; /* Token identifying terminal. */
- int pID; /* Process invoking operation. */
- int familyID; /* Family of pID. */
- int *numCharsPtr; /* Points to maximum number of characters to
- * read from terminal. Overwritten by number
- * of chars. actually returned. */
- char *buffer; /* Where to place characters that are read. */
- int *sigNumPtr; /* Overwrite this with the number of a signal
- * to generate for the calling process. 0
- * means no signal. */
- int *selectBitsPtr; /* The FS_READABLE bit in this word gets
- * updated to reflect whether or not there
- * are still more readable characters after
- * the ones returned. */
- {
- register Terminal *tPtr = (Terminal *) terminal;
- register char *dest;
- int count, result;
-
- *sigNumPtr = 0;
-
- /*
- * See if this process owns the terminal. If not, then signal it
- * and don't give it any input.
- */
-
- if (!(tPtr->flags & OWNER_FAMILY)) {
- if ((pID != tPtr->owner) && (tPtr->owner != -1)) {
- notOwner:
- *sigNumPtr = SIGTTIN;
- *numCharsPtr = 0;
- result = EINTR;
- goto done;
- }
- } else if ((familyID != tPtr->owner) && (tPtr->owner != -1)) {
- goto notOwner;
- }
-
- /*
- * Re-echo buffered characters, if so requested.
- */
-
- if (tPtr->localMode & LPENDIN) {
- int oldCharsBuffered;
-
- oldCharsBuffered = tPtr->outCharsBuffered;
- tPtr->localMode &= ~LPENDIN;
- TdRetypeInput(tPtr, tPtr->lastRemovedIn);
- if ((oldCharsBuffered == 0) && (tPtr->outCharsBuffered != 0) &&
- !(tPtr->flags & OUTPUT_OFF)) {
- (*tPtr->rawProc)(tPtr->rawData, TD_RAW_OUTPUT_READY,
- 0, (char *) NULL, 0, (char *) NULL);
- }
- }
-
- /*
- * Make sure there's information ready for the terminal. If not,
- * then block the process.
- */
-
- if (tPtr->lastBreak == tPtr->lastRemovedIn) {
- *numCharsPtr = 0;
- *selectBitsPtr &= ~FS_READABLE;
- return EWOULDBLOCK;
- }
-
- /*
- * Copy bytes from the input buffer to the caller's buffer,
- * and update the terminal's input buffer pointer.
- */
-
- count = 0;
- dest = buffer;
- while ((tPtr->lastRemovedIn != tPtr->lastBreak) &&
- (count < *numCharsPtr)) {
- register char c;
-
- NEXT(tPtr->lastRemovedIn, tPtr->inBufSize, tPtr->lastRemovedIn);
- count++;
- c = tPtr->inputBuffer[tPtr->lastRemovedIn];
- *dest = c;
- dest++;
- if (!(tPtr->sgttyb.sg_flags & (RAW|CBREAK))) {
- if (c == tPtr->tchars.t_eofc) {
- count--; /* Don't return end-of-file chars. */
- break;
- } else if ((c == '\n') || (c == tPtr->tchars.t_brkc)) {
- break;
- }
- }
- }
- *numCharsPtr = count;
- result = 0;
-
- done:
- if (tPtr->lastBreak == tPtr->lastRemovedIn) {
- *selectBitsPtr &= ~FS_READABLE;
- } else {
- *selectBitsPtr |= FS_READABLE;
- }
- return result;
- }
-
- /*
- *----------------------------------------------------------------------
- *
- * Td_PutCooked --
- *
- * Add characters to the output buffer for a terminal.
- *
- * Results:
- * The return value is always 0 (the output is grown enough to
- * hold all the output characters). The value at *numBytesPtr
- * is left unchanged to indicate that all the characters were
- * accepted, *sigNumPtr is overwritten with a signal to send
- * to the invoking process (or 0), and *selectBitsPtr is updated
- * to reflect whether the terminal's output buffer is now full.
- *
- * Side effects:
- * Output processing is performed on the characters in buffer,
- * and they are queued for output on the terminal's raw side.
- *
- *----------------------------------------------------------------------
- */
-
- /* ARGSUSED */
- int
- Td_PutCooked(terminal, numBytesPtr, buffer, sigNumPtr, selectBitsPtr)
- Td_Terminal terminal; /* Token identifying terminal. */
- int *numBytesPtr; /* Points to maximum number of characters
- * to write to terminal. Not modified
- * by this procedure. */
- register char *buffer; /* Characters to write. */
- int *sigNumPtr; /* Overwrite this with the number of a signal
- * to generate for the calling process. 0
- * means no signal. */
- int *selectBitsPtr; /* The FS_WRITABLE bit in this word gets
- * updated to reflect whether or not there
- * are still more space available in the
- * terminal's output buffer. */
- {
- register Terminal *tPtr = (Terminal *) terminal;
- register char c;
- int i, oldCharsBuffered;
-
- *sigNumPtr = 0;
-
- oldCharsBuffered = tPtr->outCharsBuffered;
- for (i = 0; i < *numBytesPtr; i++, buffer++) {
- c = *buffer;
- if ((tPtr->sgttyb.sg_flags & RAW) || (tPtr->localMode & LLITOUT)) {
- TdPutChar(tPtr, c);
- continue;
- }
- c &= 0177;
- if (c == 04) { /* End of file (^D) ignored */
- continue;
- } else if ((c == '\n') && (tPtr->sgttyb.sg_flags & CRMOD)) {
- TdPutChar(tPtr, '\r');
- TdPutChar(tPtr, '\n');
- } else {
- TdPutChar(tPtr, c);
- }
- }
- tPtr->keyIndex = tPtr->lastAddedIn;
- tPtr->keyColumn = tPtr->column;
- if (tPtr->lastAddedIn != tPtr->lastRemovedIn) {
- tPtr->lastHidden = tPtr->lastAddedIn;
- }
- if ((oldCharsBuffered == 0) && (tPtr->outCharsBuffered != 0) &&
- !(tPtr->flags & OUTPUT_OFF)) {
- (*tPtr->rawProc)(tPtr->rawData, TD_RAW_OUTPUT_READY,
- 0, (char *) NULL, 0, (char *) NULL);
- }
- if (tPtr->outCharsBuffered >= tPtr->cookedOutputLimit) {
- *selectBitsPtr &= ~FS_WRITABLE;
- } else {
- *selectBitsPtr |= FS_WRITABLE;
- }
- return 0;
- }
-
- /*
- *----------------------------------------------------------------------
- *
- * Td_ControlCooked --
- *
- * This procedure is used to invoke iocontrol operations on the
- * cooked side of a terminal driver.
- *
- * Results:
- * If the operation completed successfully then the return value
- * is zero. If the operation failed, then the return value is a
- * UNIX errno indicating what went wrong. *SigNumPtr gets
- * overwritten with the number of a signal to send to the invoking
- * process (or 0), and *selectBitsPtr gets filled in with information
- * about whether or not the terminal is now readable or writable.
- *
- * Side effects:
- * Depends on the iocontrol; see the tty(4) man page for details.
- *
- *----------------------------------------------------------------------
- */
-
- /* ARGSUSED */
- int
- Td_ControlCooked(terminal, command, format, inputSize, input, outputSizePtr,
- output, sigNumPtr, selectBitsPtr)
- Td_Terminal terminal; /* Token for terminal. */
- int command; /* Iocontrol operation to perform
- * ( e.g. TIOCGETP). */
- Fmt_Format format; /* Byte-order/alignment format */
- int inputSize; /* Size of input, in bytes. */
- char *input; /* Input buffer: contains information
- * provided by client as input to
- * operation. */
- int *outputSizePtr; /* Largest amount of output that
- * client is prepared to receive. This
- * value is overwritten with the count
- * of actual bytes returned. */
- char *output; /* Place to store output bytes;
- * provided by caller. */
- int *sigNumPtr; /* Overwrite this with the number of
- * a signal to generate for the
- * calling process. 0 means no
- * signal. */
- int *selectBitsPtr; /* Store new select state of terminal
- * here. */
- {
- register Terminal *tPtr = (Terminal *) terminal;
- int count, result;
- char *out = (char *) NIL;
- int i;
- Ioc_Owner owner;
- int oldIspeed, oldOspeed, oldFlags, oldStopc, oldStartc;
-
- /*
- * The union below describes all of the possible formats in which
- * the input area may appear.
- */
-
- typedef union {
- int i;
- char chars[20];
- struct sgttyb sgttyb;
- struct tchars tchars;
- struct ltchars ltchars;
- Ioc_Owner owner;
- struct winsize winsize;
- } InBuf;
- InBuf newInputBuf;
- register InBuf *in = (InBuf *) input;
-
- if (td_Debug) {
- printf("Td_ControlCooked: command %d\n", command);
- }
-
- if (format != FMT_MY_FORMAT) {
- /*
- * Fix up the formatting of the input buffer.
- */
- int newSize = sizeof(newInputBuf);
- if (FormatInput(command, format, inputSize, input,
- &newSize, (Address) &newInputBuf) != FMT_OK) {
- goto invalid;
- }
- in = &newInputBuf;
- inputSize = newSize;
- }
- /*
- * Save certain pieces of information about the terminal so that
- * if they change we can call the raw control procedure.
- */
-
- oldIspeed = tPtr->sgttyb.sg_ispeed;
- oldOspeed = tPtr->sgttyb.sg_ospeed;
- oldFlags = tPtr->sgttyb.sg_flags;
- oldStopc = tPtr->tchars.t_stopc;
- oldStartc = tPtr->tchars.t_startc;
-
- *sigNumPtr = 0;
- count = 0;
- switch (command) {
-
- case IOC_TTY_SET_DISCIPLINE:
- if ((inputSize != sizeof(int))
- || (in->i != NTTYDISC)) {
- goto invalid;
- }
- break;
-
- case IOC_TTY_GET_DISCIPLINE:
- if (inputSize != 0) {
- goto invalid;
- }
- i = NTTYDISC;
- out = (char *) &i;
- count = sizeof(int);
- break;
-
- case IOC_TTY_GETP:
- if (inputSize != 0) {
- goto invalid;
- }
- out = (char *) &tPtr->sgttyb;
- count = sizeof(struct sgttyb);
- break;
-
- case IOC_TTY_SETP:
- /*
- * Technically, this code should delay until all characters
- * currently buffered for output have been printed, but
- * there's no easy way to do that here: ioctls must complete
- * immediately.
- */
- TdFlushInput(tPtr);
- case IOC_TTY_SETN:
- if (inputSize != sizeof(struct sgttyb)) {
- goto invalid;
- }
- if ((tPtr->sgttyb.sg_flags ^ in->sgttyb.sg_flags) & RAW) {
- /*
- * Going into or out of raw mode; always flush input
- * buffer.
- */
-
- TdFlushInput(tPtr);
- }
- tPtr->sgttyb = in->sgttyb;
- break;
-
- case IOC_TTY_EXCL:
- if (inputSize != 0) {
- goto invalid;
- }
- tPtr->flags |= EXCLUSIVE;
- break;
-
- case IOC_TTY_NXCL:
- if (inputSize != 0) {
- goto invalid;
- }
- tPtr->flags &= ~EXCLUSIVE;
- break;
-
- case IOC_TTY_HUP_ON_CLOSE: /* Not implemented. */
- goto invalid;
-
- case IOC_TTY_FLUSH: {
- /*
- * For compatibility with TIOCFLUSH, we accept one
- * integer argument which has the FREAD and FWRITE bits in it.
- */
- int flags;
- if (inputSize == 0) {
- flags = 0;
- } else {
- flags = in->i;
- }
- #ifndef FREAD
- #define FREAD 0x1
- #define FWRITE 0x2
- #endif
- if (flags == 0) {
- flags = FREAD|FWRITE;
- }
- if (flags & FREAD) {
- TdFlushInput(tPtr);
- }
- if (flags & FWRITE) {
- TdFlushOutput(tPtr);
- }
- break;
- }
- case IOC_TTY_INSERT_CHAR:
- if (inputSize != 1) {
- goto invalid;
- }
- Td_PutRaw((Td_Terminal) tPtr, 1, &in->chars[0]);
- break;
-
- case IOC_TTY_SET_BREAK:
- (*tPtr->rawProc)(tPtr->rawData, TD_RAW_START_BREAK,
- 0, (char *) NULL, 0, (char *) NULL);
- break;
-
- case IOC_TTY_CLEAR_BREAK:
- (*tPtr->rawProc)(tPtr->rawData, TD_RAW_START_BREAK,
- 0, (char *) NULL, 0, (char *) NULL);
- break;
-
- case IOC_TTY_SET_DTR:
- (*tPtr->rawProc)(tPtr->rawData, TD_RAW_SET_DTR,
- 0, (char *) NULL, 0, (char *) NULL);
- break;
-
- case IOC_TTY_CLEAR_DTR:
- (*tPtr->rawProc)(tPtr->rawData, TD_RAW_CLEAR_DTR,
- 0, (char *) NULL, 0, (char *) NULL);
- break;
-
- case IOC_GET_OWNER:
- if (inputSize != 0) {
- goto invalid;
- }
- owner.id = tPtr->owner;
- if (tPtr->flags & OWNER_FAMILY) {
- owner.procOrFamily = IOC_OWNER_FAMILY;
- } else {
- owner.procOrFamily = IOC_OWNER_PROC;
- }
- out = (char *) &owner;
- count = sizeof(owner);
- break;
-
- case IOC_SET_OWNER:
- if (inputSize != sizeof(Ioc_Owner)) {
- goto invalid;
- }
- tPtr->owner = in->owner.id;
- if (in->owner.procOrFamily == IOC_OWNER_FAMILY) {
- tPtr->flags |= OWNER_FAMILY;
- } else {
- tPtr->flags &= ~OWNER_FAMILY;
- }
- break;
-
- case IOC_NUM_READABLE:
- i = tPtr->lastBreak - tPtr->lastRemovedIn;
- if (i < 0) {
- i += tPtr->inBufSize;
- }
- out = (char *) &i;
- count = sizeof(int);
- break;
-
- case IOC_TTY_GET_TCHARS:
- if (inputSize != 0) {
- goto invalid;
- }
- out = (char *) &tPtr->tchars;
- count = sizeof(struct tchars);
- break;
-
- case IOC_TTY_SET_TCHARS:
- if (inputSize != sizeof(struct tchars)) {
- goto invalid;
- }
- tPtr->tchars = in->tchars;
- break;
-
- case IOC_TTY_BIS_LM:
- if (inputSize != sizeof(int)) {
- goto invalid;
- }
- tPtr->localMode |= in->i;
- break;
-
- case IOC_TTY_BIC_LM:
- if (inputSize != sizeof(int)) {
- goto invalid;
- }
- tPtr->localMode &= ~in->i;
- break;
-
- case IOC_TTY_SET_LM:
- if (inputSize != sizeof(int)) {
- goto invalid;
- }
- tPtr->localMode = in->i;
- break;
-
- case IOC_TTY_GET_LM:
- if (inputSize != 0) {
- goto invalid;
- }
- out = (char *) &tPtr->localMode;
- count = sizeof(int);
- break;
-
- case IOC_TTY_SET_LTCHARS:
- if (inputSize != sizeof(struct ltchars)) {
- goto invalid;
- }
- tPtr->ltchars = in->ltchars;
- break;
-
- case IOC_TTY_GET_LTCHARS:
- if (inputSize != 0) {
- goto invalid;
- }
- out = (char *) &tPtr->ltchars;
- count = sizeof(struct ltchars);
- break;
-
- case IOC_GET_FLAGS:
- i = 0;
- out = (char *) &i;
- count = sizeof(int);
- break;
- case IOC_SET_FLAGS:
- case IOC_SET_BITS:
- case IOC_CLEAR_BITS:
- case IOC_TTY_NOT_CONTROL_TTY:
- break;
-
- case IOC_TTY_GET_WINDOW_SIZE:
- if (inputSize != 0) {
- goto invalid;
- }
- out = (char *) &tPtr->winsize;
- count = sizeof(struct winsize);
- break;
-
- case IOC_TTY_SET_WINDOW_SIZE: {
- Td_Signal signalInfo;
- if (inputSize != sizeof(struct winsize)) {
- goto invalid;
- }
- tPtr->winsize = in->winsize;
- signalInfo.sigNum = SIGWINCH;
- signalInfo.groupID = tPtr->owner;
- (*tPtr->cookedProc)(tPtr->cookedData, TD_COOKED_SIGNAL,
- sizeof(signalInfo), (char *) &signalInfo,
- 0, (char *) NULL);
- break;
- }
-
- default:
- goto invalid;
- }
- /*
- * Fix up output buffer for the client. At this point
- * count = size of output data
- * out = pointer to output data
- * Here we rely on the Fmt_ library doing essentially a bcopy
- * if our format and the client's format are the same.
- */
- if (count != 0) {
- result = FormatOutput(command, format, count, out,
- outputSizePtr, output);
- if (result != FMT_OK) {
- result = EINVAL;
- if (td_Debug) {
- printf("Td_ControlCooked: command %d invalid output\n", command);
- }
- }
- } else {
- *outputSizePtr = 0;
- result = 0;
- }
-
- /*
- * Call the raw control procedure if anything changed that it needs
- * to know about.
- */
-
- if ((oldIspeed != tPtr->sgttyb.sg_ispeed)
- || (oldOspeed != tPtr->sgttyb.sg_ospeed)) {
- Td_BaudRate baud, baud2;
-
- baud.ispeed = tPtr->sgttyb.sg_ispeed;
- baud.ospeed = tPtr->sgttyb.sg_ospeed;
- if ((*tPtr->rawProc)(tPtr->rawData, TD_RAW_SET_BAUD_RATE,
- sizeof(baud), (char *) &baud,
- sizeof(baud2), (char *) &baud2) == sizeof(baud2)) {
-
- /*
- * Device has overridden the baud-rate change; take its advice.
- */
-
- tPtr->sgttyb.sg_ispeed = baud2.ispeed;
- tPtr->sgttyb.sg_ospeed = baud2.ospeed;
- }
- }
- if (((oldFlags & RAW) != (tPtr->sgttyb.sg_flags & RAW))
- || (oldStopc != tPtr->tchars.t_stopc)
- || (oldStartc != tPtr->tchars.t_startc)) {
- Td_FlowChars flow;
-
- if (tPtr->sgttyb.sg_flags & RAW) {
- flow.stop = flow.start = -1;
- } else {
- flow.stop = tPtr->tchars.t_stopc;
- flow.start = tPtr->tchars.t_startc;
- }
- (*tPtr->rawProc)(tPtr->rawData, TD_RAW_FLOW_CHARS,
- sizeof(flow), (char *) &flow, 0, (char *) NULL);
- }
-
- /*
- * Setting the select bits is a bit tricky: if LPENDIN is set, then
- * we need to find out when the next read is done. So, make the device
- * appear to be readable even if it isn't. Otherwise, we won't be told
- * when the device is read.
- */
-
- done:
- *selectBitsPtr = 0;
- if ((tPtr->lastBreak != tPtr->lastRemovedIn)
- || (tPtr->localMode & LPENDIN)) {
- *selectBitsPtr |= FS_READABLE;
- }
- if (tPtr->outCharsBuffered < tPtr->cookedOutputLimit) {
- *selectBitsPtr |= FS_WRITABLE;
- }
- return result;
-
- invalid:
- if (td_Debug) {
- printf("Td_ControlCooked: command %d invalid input\n", command);
- }
- *outputSizePtr = 0;
- result = EINVAL;
- goto done;
- }
-
- /*
- *----------------------------------------------------------------------
- *
- * FormatInput --
- *
- * Re-format the input buffer of an I/O control. This is required
- * if the client is on a host with a different byte order/alignment.
- * This uses the Fmt_Convert library routine.
- *
- * Results:
- * This returns zero if all goes well.
- * Otherwise a FMT_ error code is returned.
- *
- * Side effects:
- * The reformatted input is put into newBuffer. The true size of
- * the data in this buffer is returned in *newInputSizePtr.
- *
- *----------------------------------------------------------------------
- */
-
- static int
- FormatInput(command, format, inputSize, input, newInputSizePtr, newInput)
- int command; /* I/O Control command */
- Fmt_Format format; /* Format of client host */
- int inputSize; /* Size of input buffer */
- Address input; /* Input buffer in client format */
- int *newInputSizePtr; /* In/Out - Size of new input buffer */
- Address newInput; /* Out - Input buffer in our format */
- {
- int status = FMT_OK;
- char *fmtString = "";
-
- switch (command) {
-
- case IOC_TTY_GET_DISCIPLINE:
- case IOC_TTY_GETP:
- case IOC_TTY_EXCL:
- case IOC_TTY_NXCL:
- case IOC_GET_OWNER:
- case IOC_TTY_GET_TCHARS:
- case IOC_TTY_GET_LM:
- case IOC_TTY_GET_LTCHARS:
- case IOC_TTY_GET_WINDOW_SIZE:
- case IOC_TTY_NOT_CONTROL_TTY:
- default:
- *newInputSizePtr = 0;
- goto noconversion;
-
- case IOC_TTY_FLUSH:
- /*
- * Optional int
- */
- if (inputSize == 0) {
- *newInputSizePtr = 0;
- goto noconversion;
- } else {
- fmtString = "w";
- }
- break;
-
- case IOC_TTY_SET_DISCIPLINE:
- case IOC_TTY_BIS_LM:
- case IOC_TTY_BIC_LM:
- case IOC_TTY_SET_LM:
- /*
- * One int
- */
- fmtString = "w";
- break;
-
- case IOC_TTY_SETP:
- case IOC_TTY_SETN:
- /*
- * struct sgttyb
- */
- fmtString = "{b4h}";
- break;
-
- case IOC_TTY_INSERT_CHAR:
- /*
- * One char
- */
- fmtString = "b";
- break;
-
- case IOC_SET_OWNER:
- /*
- * Ioc_Owner
- */
- fmtString = "{w2}";
- break;
-
- case IOC_TTY_SET_TCHARS:
- /*
- * struct tchars
- */
- fmtString = "{b6}";
- break;
-
- case IOC_TTY_SET_LTCHARS:
- /*
- * struct ltchars
- */
- fmtString = "{b6}";
- break;
-
-
- case IOC_TTY_SET_WINDOW_SIZE: {
- /*
- * struct winsize
- */
- fmtString = "{h4}";
- break;
- }
-
- }
- status = Fmt_Convert(fmtString, format, &inputSize, input, FMT_MY_FORMAT,
- newInputSizePtr, newInput);
- noconversion:
- return(status);
- }
-
- /*
- *----------------------------------------------------------------------
- *
- * FormatOutput --
- *
- * Re-format the output buffer of an I/O control.
- * This uses the Fmt_Convert library routine.
- *
- * Results:
- * This returns zero if all goes well.
- * Otherwise a FMT_ error code is returned.
- *
- * Side effects:
- * The reformatted output is put into newOutput. The true size of
- * the data in this buffer is returned in *newOutputSizePtr.
- *
- *----------------------------------------------------------------------
- */
-
- static int
- FormatOutput(command, format, outputSize, output, newOutputSizePtr, newOutput)
- int command; /* I/O Control command */
- Fmt_Format format; /* Format of client host */
- int outputSize; /* Size of input buffer (in our format) */
- Address output; /* Output buffer in our format */
- int *newOutputSizePtr; /* In/Out - Size of new output buffer */
- Address newOutput; /* Out - Output buffer in the client's format */
- {
- int status = FMT_OK;
- char *fmtString = "";
-
- switch (command) {
-
- case IOC_TTY_SET_DISCIPLINE:
- case IOC_TTY_SETP:
- case IOC_TTY_SETN:
- case IOC_TTY_EXCL:
- case IOC_TTY_NXCL:
- case IOC_TTY_FLUSH:
- case IOC_TTY_INSERT_CHAR:
- case IOC_SET_OWNER:
- case IOC_TTY_SET_TCHARS:
- case IOC_TTY_BIS_LM:
- case IOC_TTY_BIC_LM:
- case IOC_TTY_SET_LM:
- case IOC_TTY_SET_LTCHARS:
- case IOC_TTY_NOT_CONTROL_TTY:
- default:
- *newOutputSizePtr = 0;
- goto noconversion;
-
- case IOC_TTY_GET_DISCIPLINE:
- case IOC_NUM_READABLE:
- case IOC_TTY_GET_LM:
- case IOC_GET_FLAGS:
- /*
- * One int
- */
- fmtString = "w";
- break;
-
- case IOC_TTY_GETP:
- /*
- * struct sgttyb
- */
- fmtString = "{b4h}";
- break;
-
- case IOC_GET_OWNER:
- /*
- * Ioc_Owner
- */
- fmtString = "{w2}";
- break;
-
- case IOC_TTY_GET_TCHARS:
- /*
- * struct tchars
- */
- fmtString = "{b6}";
- break;
-
- case IOC_TTY_GET_LTCHARS:
- /*
- * struct ltchars
- */
- fmtString = "{b6}";
- break;
-
- case IOC_TTY_GET_WINDOW_SIZE:
- /*
- * struct winsize
- */
- fmtString = "{h4}";
- break;
- }
- status = Fmt_Convert(fmtString, FMT_MY_FORMAT, &outputSize, output, format,
- newOutputSizePtr, newOutput);
- noconversion:
- return(status);
- }
-
- /*
- *----------------------------------------------------------------------
- *
- * Td_GetRaw --
- *
- * Retrieve characters that are ready to be output from the
- * terminal driver to the raw device.
- *
- * Results:
- * The return value is a count of the number of characters
- * actually returned at buffer. This will be less than or
- * equal to numChars. A return value of 0 indicates that
- * there are no characters in terminal's output buffer or
- * that output has been disabled.
- *
- * Side effects:
- * Characters are removed from the terminal's output buffer,
- * and the cooked side may be notified that the terminal is
- * writable again.
- *
- *----------------------------------------------------------------------
- */
-
- int
- Td_GetRaw(terminal, numChars, buffer)
- Td_Terminal terminal; /* Token identifying terminal. */
- int numChars; /* Maximum number of characters to read
- * from terminal's output buffer. */
- register char *buffer; /* Where to place characters that are read. */
- {
- register Terminal *tPtr = (Terminal *) terminal;
- int count;
-
- if (tPtr->flags & OUTPUT_OFF) {
- return 0;
- }
- for (count = 0; count < numChars; count++, buffer++) {
- if (tPtr->lastRemovedOut == tPtr->lastAddedOut) {
- break;
- }
- NEXT(tPtr->lastRemovedOut, tPtr->outBufSize, tPtr->lastRemovedOut);
- *buffer = tPtr->outputBuffer[tPtr->lastRemovedOut];
- }
- tPtr->outCharsBuffered -= count;
- if (tPtr->outCharsBuffered < tPtr->cookedOutputLimit) {
- (*tPtr->cookedProc)(tPtr->cookedData, TD_COOKED_WRITES_OK,
- 0, (char *) NULL, 0, (char *) NULL);
- }
- return count;
- }
-
- /*
- *----------------------------------------------------------------------
- *
- * Td_PutRaw --
- *
- * This procedure is invoked when characters arrive from the
- * raw device associated with the terminal (e.g., from the
- * keyboard). It adds them to the input buffer of the terminal
- * and does appropriate line editing etc.
- *
- * Results:
- * None.
- *
- * Side effects:
- * Characters are added to the input buffer, and may be made
- * available on the cooked side of the terminal. Echoed
- * characters get added to the output buffer, which could result
- * in a call to the raw control procedure.
- *
- *----------------------------------------------------------------------
- */
-
- void
- Td_PutRaw(terminal, numChars, buffer)
- Td_Terminal terminal; /* Token identifying terminal. */
- int numChars; /* Number of characters to process. */
- char *buffer; /* Characters that were ostensibly typed
- * on the raw device's keyboard. */
- {
- register Terminal *tPtr = (Terminal *) terminal;
- int next, oldCharsBuffered;
- register char c = '\0'; /* dummy initial value */
- Td_Signal signalInfo;
-
- oldCharsBuffered = tPtr->outCharsBuffered;
- tPtr->localMode &= ~LFLUSHO;
-
- /*
- * According to the 4.3 BSD manual page, we should re-echo everything
- * in the input buffer if LPENDIN is set here. But this appears to
- * produce the wrong results and I suspect that it isn't even
- * implemented in BSD. So it's not implemented here either.
- */
-
- for ( ; numChars > 0; numChars--, buffer++) {
- c = *buffer;
-
- /*
- * Skip all further processing if in raw mode.
- */
-
- if (tPtr->sgttyb.sg_flags & RAW) {
- goto addToBuffer;
- }
- c &= 0x7f;
-
- /*
- * Handle flow-control characters.
- */
-
-
- if (c == tPtr->tchars.t_stopc) {
- if (tPtr->flags & OUTPUT_OFF) {
- if (c == tPtr->tchars.t_startc) {
- goto restartOutput;
- }
- } else {
- tPtr->flags |= OUTPUT_OFF;
- }
- continue;
- } else if (c == tPtr->tchars.t_startc) {
- restartOutput:
- tPtr->flags &= ~OUTPUT_OFF;
- if (tPtr->outCharsBuffered != 0) {
- (*tPtr->rawProc)(tPtr->rawData, TD_RAW_OUTPUT_READY,
- 0, (char *) NULL, 0, (char *) NULL);
- }
- continue;
- } else if ((tPtr->flags & OUTPUT_OFF) && !(tPtr->localMode & LDECCTQ)) {
- tPtr->flags &= ~OUTPUT_OFF;
- if (tPtr->outCharsBuffered != 0) {
- (*tPtr->rawProc)(tPtr->rawData, TD_RAW_OUTPUT_READY,
- 0, (char *) NULL, 0, (char *) NULL);
- }
- }
-
- /*
- * If the last character typed was a "quote" character, then
- * just add the new character to the input buffer without
- * additional processing.
- */
-
- if (tPtr->flags & LITERAL_NEXT) {
- tPtr->flags &= ~LITERAL_NEXT;
- goto addToBuffer;
- }
-
- /*
- * Handle output-flushing character. Clearing oldCharsBuffered
- * is essential, otherwise, the raw client won't be notified if
- * characters are added to the output buffer in this procedure.
- */
-
- if (c == tPtr->ltchars.t_flushc) {
- if (tPtr->localMode & LFLUSHO) {
- tPtr->localMode &= ~LFLUSHO;
- } else {
- TdFlushOutput(tPtr);
- oldCharsBuffered = 0;
- TdEcho(tPtr, c);
- TdRetypeInput(tPtr, tPtr->lastRemovedIn);
- tPtr->localMode |= LFLUSHO;
- }
- continue;
- }
-
- /*
- * Handle line-editing characters such as erase and kill.
- */
-
- if ((c == '\r') && (tPtr->sgttyb.sg_flags & CRMOD)) {
- c = '\n';
- }
- if (!(tPtr->sgttyb.sg_flags & CBREAK)) {
- if (c == tPtr->sgttyb.sg_erase) { /* Backspace. */
- if (tPtr->lastAddedIn != tPtr->lastBreak) {
- TdBackspace(tPtr);
- }
- continue;
- } else if (c == tPtr->ltchars.t_werasc) { /* Delete word. */
- int gotNonSpace = 0;
-
- while (tPtr->lastAddedIn != tPtr->lastBreak) {
- if (isspace(tPtr->inputBuffer[tPtr->lastAddedIn])) {
- if (gotNonSpace) {
- break;
- }
- } else {
- gotNonSpace = 1;
- }
- TdBackspace(tPtr);
- }
- continue;
- } else if (c == tPtr->sgttyb.sg_kill) { /* Delete line. */
- if ((tPtr->lastHidden != -1) || !(tPtr->localMode & LCRTKIL)) {
- TdEcho(tPtr, c);
- TdEcho(tPtr, '\n');
- tPtr->lastAddedIn = tPtr->lastBreak;
- tPtr->lastHidden = -1;
- } else {
- while (tPtr->lastAddedIn != tPtr->lastBreak) {
- TdBackspace(tPtr);
- }
- }
- continue;
- } else if (c == tPtr->ltchars.t_rprntc) { /* Re-echo all. */
- TdEcho(tPtr, c);
- TdEcho(tPtr, '\n');
- TdRetypeInput(tPtr, tPtr->lastRemovedIn);
- continue;
- }
- }
-
- if (c == tPtr->ltchars.t_lnextc) {
- tPtr->flags |= LITERAL_NEXT;
- continue;
- }
-
- /*
- * Generate signals in response to certain input characters. If this
- * isn't a signal character, then officially add it to the input
- * buffer.
- */
-
- if (c == tPtr->tchars.t_intrc) {
- signalInfo.sigNum = SIGINT;
- sendSignal:
- signalInfo.groupID = tPtr->owner;
- (*tPtr->cookedProc)(tPtr->cookedData, TD_COOKED_SIGNAL,
- sizeof(signalInfo), (char *) &signalInfo,
- 0, (char *) NULL);
- TdFlushInput(tPtr);
- TdFlushOutput(tPtr);
- oldCharsBuffered = 0;
- goto echo;
- } else if (c == tPtr->tchars.t_quitc) {
- signalInfo.sigNum = SIGQUIT;
- goto sendSignal;
- } else if (c == tPtr->ltchars.t_suspc) {
- signalInfo.sigNum = SIGTSTP;
- goto sendSignal;
- }
-
- /*
- * If the buffer is full, then reallocate it with a size twice as
- * large. Then add the character to the buffer.
- */
-
- addToBuffer:
- NEXT(tPtr->lastAddedIn, tPtr->inBufSize, next);
- if (next == tPtr->lastRemovedIn) {
- char *newBuffer;
- int src, dst;
-
- newBuffer = malloc((unsigned) 2*tPtr->inBufSize);
- for (src = tPtr->lastRemovedIn, dst = 0; src != tPtr->lastAddedIn; ) {
- NEXT(src, tPtr->inBufSize, src);
- dst += 1;
- newBuffer[dst] = tPtr->inputBuffer[src];
- }
- tPtr->lastBreak -= tPtr->lastRemovedIn;
- if (tPtr->lastBreak < 0) {
- tPtr->lastBreak += tPtr->inBufSize;
- }
- if (tPtr->lastHidden != -1) {
- tPtr->lastHidden -= tPtr->lastRemovedIn;
- if (tPtr->lastHidden < 0) {
- tPtr->lastHidden += tPtr->inBufSize;
- }
- }
- tPtr->inputBuffer = newBuffer;
- tPtr->inBufSize *= 2;
- tPtr->lastRemovedIn = 0;
- tPtr->lastAddedIn = dst;
- NEXT(dst, tPtr->inBufSize, next);
- }
- tPtr->inputBuffer[next] = c;
- tPtr->lastAddedIn = next;
-
- /*
- * Echo.
- */
-
- echo:
- if ((tPtr->sgttyb.sg_flags & ECHO) && !(tPtr->sgttyb.sg_flags & RAW)) {
- if (tPtr->flags & BS_IN_PROGRESS) {
- TdPutChar(tPtr, '/');
- tPtr->flags &= ~BS_IN_PROGRESS;
- }
- TdEcho(tPtr, c);
- }
- }
-
- /*
- * Are there any characters that are ready for reading? If so,
- * change the terminal's state to be readable and notify the
- * cooked side.
- */
-
- if ((tPtr->sgttyb.sg_flags & (RAW|CBREAK)) || (c == tPtr->tchars.t_eofc) ||
- (c == tPtr->tchars.t_brkc) || (c == '\n')) {
- tPtr->lastBreak = tPtr->lastAddedIn;
- tPtr->lastHidden = -1;
- tPtr->keyIndex = tPtr->lastAddedIn;
- tPtr->keyColumn = tPtr->column;
- (*tPtr->cookedProc)(tPtr->cookedData, TD_COOKED_READS_OK,
- 0, (char *) NULL, 0, (char *) NULL);
- }
-
- /*
- * If the output buffer just became non-empty, then notify the
- * raw control procedure.
- */
-
- if ((oldCharsBuffered == 0) && (tPtr->outCharsBuffered != 0) &&
- !(tPtr->flags & OUTPUT_OFF)) {
- (*tPtr->rawProc)(tPtr->rawData, TD_RAW_OUTPUT_READY,
- 0, (char *) NULL, 0, (char *) NULL);
- }
- }
-
- /*
- *----------------------------------------------------------------------
- *
- * Td_ControlRaw --
- *
- * This procedure is used to tell the terminal driver that
- * certain special things happened on the raw side of the
- * terminal, such as a hangup or break.
- *
- * Results:
- * None.
- *
- * Side effects:
- * Depends on the operation; see the man page for details
- * on what commands may be invoked.
- *
- *----------------------------------------------------------------------
- */
-
- /* ARGSUSED */
- void
- Td_ControlRaw(terminal, operation)
- Td_Terminal terminal; /* Token for terminal. */
- int operation; /* What just happened: TD_BREAK etc. */
- {
- register Terminal *tPtr = (Terminal *) terminal;
-
- switch (operation) {
- case TD_BREAK: {
-
- /*
- * Reset some of the terminal state, such as flow control,
- * then pretend an interrupt character was typed.
- */
-
- tPtr->flags &= ~(OUTPUT_OFF | BS_IN_PROGRESS | LITERAL_NEXT);
- if (tPtr->sgttyb.sg_flags & RAW) {
- char c = 0;
- Td_PutRaw(terminal, 1, &c);
- } else if (tPtr->tchars.t_intrc != -1) {
- Td_PutRaw(terminal, 1, &tPtr->tchars.t_intrc);
- }
- if (tPtr->outCharsBuffered != 0) {
- (*tPtr->rawProc)(tPtr->rawData, TD_RAW_OUTPUT_READY,
- 0, (char *) NULL, 0, (char *) NULL);
- }
- break;
- }
- }
- }
-
- /*
- *----------------------------------------------------------------------
- *
- * TdPutChar --
- *
- * Add a character to the output buffer associated with a
- * terminal, and keep track of the current column while outputting
- * the character. This routine also substitutes spaces for tabs,
- * if that's the mode the terminal is in.
- *
- * Results:
- * None.
- *
- * Side effects:
- * TPtr->column gets updated and stuff gets added to the terminal's
- * output buffer. The output buffer will get grown if necessary.
- *
- *----------------------------------------------------------------------
- */
-
- static void
- TdPutChar(tPtr, c)
- register Terminal *tPtr; /* Terminal on which to output. */
- char c; /* Character to output. */
- {
- /*
- * Ignore the character if output is being flushed.
- */
-
- if (tPtr->localMode & LFLUSHO) {
- return;
- }
-
- /*
- * Grow the output buffer if there isn't enough space for the
- * largest amount of information this procedure might want to
- * add to it.
- */
-
- if ((tPtr->outCharsBuffered + 8) >= tPtr->outBufSize) {
- char *newBuffer;
- int dst;
-
- newBuffer = malloc((unsigned) (2*tPtr->outBufSize));
- for (dst = 0; tPtr->lastRemovedOut != tPtr->lastAddedOut; ) {
- dst += 1;
- NEXT(tPtr->lastRemovedOut, tPtr->outBufSize, tPtr->lastRemovedOut);
- newBuffer[dst] = tPtr->outputBuffer[tPtr->lastRemovedOut];
- }
- tPtr->outputBuffer = newBuffer;
- tPtr->outBufSize *= 2;
- tPtr->lastRemovedOut = 0;
- tPtr->lastAddedOut = dst;
- }
-
- /*
- * Update column position, and add character(s) to the buffer.
- */
-
- if (isprint(c)) {
- tPtr->column += 1;
- } else if (c == '\r') {
- tPtr->column = 0;
- } else if (c == '\t') {
- int count = 8 - (tPtr->column & 07);
-
- tPtr->column += count;
- if ((tPtr->sgttyb.sg_flags & TBDELAY) == XTABS) {
- for ( ; count > 0; count--) {
- NEXT(tPtr->lastAddedOut, tPtr->outBufSize, tPtr->lastAddedOut);
- tPtr->outputBuffer[tPtr->lastAddedOut] = ' ';
- tPtr->outCharsBuffered++;
- }
- return;
- }
- } else if (c == '\b') {
- tPtr->column -= 1;
- }
- NEXT(tPtr->lastAddedOut, tPtr->outBufSize, tPtr->lastAddedOut);
- tPtr->outputBuffer[tPtr->lastAddedOut] = c;
- tPtr->outCharsBuffered++;
- }
-
- /*
- *----------------------------------------------------------------------
- *
- * TdEcho --
- *
- * Echo a character on a terminal, if echoing is enabled.
- *
- * Results:
- * None.
- *
- * Side effects:
- * The appropriate echo sequence for c gets added to the
- * terminal's output buffer.
- *
- *----------------------------------------------------------------------
- */
-
- static void
- TdEcho(tPtr, c)
- register Terminal *tPtr; /* Terminal for which to echo. */
- register char c; /* Character to echo. */
- {
- if (!(tPtr->sgttyb.sg_flags & ECHO)) {
- return;
- }
- if (isprint(c)) {
- TdPutChar(tPtr, c);
- } else if (c == '\n') {
- if (tPtr->sgttyb.sg_flags & CRMOD) {
- TdPutChar(tPtr, '\r');
- }
- TdPutChar(tPtr, '\n');
- } else if (c == '\t') {
- TdPutChar(tPtr, c);
- } else if (c == 04) {
- /* Don't echo control-D's. */
- } else if (tPtr->localMode & LCTLECH) {
- TdPutChar(tPtr, '^');
- if (c == 0177) {
- TdPutChar(tPtr, '?');
- } else {
- TdPutChar(tPtr, c + 'A' - 1);
- }
- } else {
- TdPutChar(tPtr, c);
- }
- }
-
- /*
- *----------------------------------------------------------------------
- *
- * TdRetypeInput --
- *
- * This procedure is called to re-echo all of the characters in
- * the input buffer.
- *
- * Results:
- * None.
- *
- * Side effects:
- * Characters get added to the terminal's output buffer.
- *
- *----------------------------------------------------------------------
- */
-
- static void
- TdRetypeInput(tPtr, start)
- register Terminal *tPtr; /* Which terminal to re-echo for. */
- int start; /* Index within tPtr's buffer: start
- * echoing at the character AFTER this one. */
- {
- tPtr->keyIndex = start;
- tPtr->keyColumn = tPtr->column;
- while (start != tPtr->lastAddedIn) {
- NEXT(start, tPtr->inBufSize, start);
- TdEcho(tPtr, tPtr->inputBuffer[start]);
- }
- tPtr->lastHidden = -1;
- }
-
- /*
- *----------------------------------------------------------------------
- *
- * TdBackspace --
- *
- * Using mode information from tPtr, output the appropriate
- * sequence to backspace over the most recently typed character
- * in tPtr's input buffer. Also remove the character from
- * the input buffer.
- *
- * Results:
- * None.
- *
- * Side effects:
- * TPtr's input buffer ends up with less characters in it,
- * and stuff gets added to the output buffer.
- *
- *----------------------------------------------------------------------
- */
-
- static void
- TdBackspace(tPtr)
- register Terminal *tPtr; /* Terminal to backspace. */
- {
- if (tPtr->sgttyb.sg_flags & ECHO) {
- if (tPtr->localMode & LCRTBS) {
- int count;
- char c;
-
- /*
- * CRT-style backspacing: the character can actually be erased.
- * Figure out how wide the character was, then back over it one
- * space at a time. If there's output intervening between us and
- * the next character to erase, then first re-echo everything.
- */
-
- if (tPtr->lastAddedIn == tPtr->lastHidden) {
- c = tPtr->ltchars.t_rprntc;
- if ((c & 0377) == 0377) {
- c = ltcharsDefault.t_rprntc;
- }
- TdEcho(tPtr, c);
- TdEcho(tPtr, '\n');
- TdRetypeInput(tPtr, tPtr->lastRemovedIn);
- tPtr->lastHidden = -1;
- }
- c = tPtr->inputBuffer[tPtr->lastAddedIn];
- if (isprint(c)) {
- count = 1;
- } else {
- int i, pos;
- char c2;
-
- /*
- * Anything besides a normal printing character is tricky. Tabs
- * are particularly nasty. To figure out how much to erase,
- * work forwards from a known position, computing the position
- * of the character just before the one being erased.
- */
-
- i = tPtr->keyIndex;
- pos = tPtr->keyColumn;
- while (TRUE) {
- NEXT(i, tPtr->inBufSize, i);
- if (i == tPtr->lastAddedIn) {
- break;
- }
- c2 = tPtr->inputBuffer[i];
- if (isprint(c2)) {
- pos++;
- } else if (c2 == '\t') {
- pos = (pos + 8) & ~07;
- } else if (((c2 == '\n') && !(tPtr->sgttyb.sg_flags & CRMOD))
- || (c2 == 04)) {
- /* No change to position. */
- } else if (tPtr->localMode & LCTLECH) {
- pos += 2;
- } else if (c2 == '\b') {
- pos -= 1;
- }
- }
- count = tPtr->column - pos;
- }
-
- for ( ; count > 0; count--) {
- if (tPtr->localMode & LCRTERA) {
- TdPutChar(tPtr, '\b');
- TdPutChar(tPtr, ' ');
- }
- TdPutChar(tPtr, '\b');
- }
- } else if (tPtr->localMode & LPRTERA) {
-
- /*
- * Hardcopy terminal: backspace by outputting erased characters
- * between "\" and "/" delimiters.
- */
-
- if (!(tPtr->flags & BS_IN_PROGRESS)) {
- TdPutChar(tPtr, '\\');
- tPtr->flags |= BS_IN_PROGRESS;
- }
- TdEcho(tPtr, tPtr->inputBuffer[tPtr->lastAddedIn]);
- } else {
-
- /*
- * Old-style backspace: just echo the backspace character.
- */
-
- TdEcho(tPtr, tPtr->sgttyb.sg_erase);
- }
- }
- PREV(tPtr->lastAddedIn, tPtr->inBufSize, tPtr->lastAddedIn);
- }
-
- /*
- *----------------------------------------------------------------------
- *
- * TdFlushInput --
- *
- * Empty the input buffer associated with a terminal.
- *
- * Results:
- * None.
- *
- * Side effects:
- * TPtr's input buffer is cleared.
- *
- *----------------------------------------------------------------------
- */
-
- static void
- TdFlushInput(tPtr)
- register Terminal *tPtr; /* Terminal to flush. */
- {
- tPtr->lastAddedIn = tPtr->lastRemovedIn = 0;
- tPtr->lastBreak = tPtr->keyIndex = 0;
- tPtr->lastHidden = -1;
- tPtr->keyColumn = tPtr->column;
- tPtr->flags &= ~(LITERAL_NEXT|BS_IN_PROGRESS);
- }
-
- /*
- *----------------------------------------------------------------------
- *
- * TdFlushOutput --
- *
- * Empty the output queue for a terminal.
- *
- * Results:
- * None.
- *
- * Side effects:
- * The output buffer for tPtr is emptied, and the terminal's raw
- * control procedure is invoked to empty any other buffers down
- * the line. The cooked side gets notified that the terminal
- * is now writable.
- *
- *----------------------------------------------------------------------
- */
-
- static void
- TdFlushOutput(tPtr)
- register Terminal *tPtr; /* Terminal to flush. */
- {
- tPtr->lastAddedOut = tPtr->lastRemovedOut = 0;
- tPtr->outCharsBuffered = 0;
- (*tPtr->rawProc)(tPtr->rawData, TD_RAW_FLUSH_OUTPUT, 0,
- (char *) NULL, 0, (char *) NULL);
- (*tPtr->cookedProc)(tPtr->cookedData, TD_COOKED_WRITES_OK, 0,
- (char *) NULL, 0, (char *) NULL);
- }
-